package ui;

import java.awt.BorderLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;

import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JSeparator;

import org.lwjgl.LWJGLException;

import primatives.PointND;

import tests.HistogramViewer;
import triangulation.SimpleTriangulation;
import util.DataGenerator;
import util.PointCloudReader;
import util.PointCloudWriter;
import util.Resampler;
import util.STLTriangle;


public class Window_MainApp extends JFrame
{
	/**
	 * 
	 */
	private static final long serialVersionUID = -3747766541320776535L;

	final JFileChooser fc = new JFileChooser();
	
	public OptionsPanel searchPanel;
	public GeoCanvas canvas;
	public JMenuBar menu_bar;
	public JMenu menu_file;
	public JMenu menu_histograms;

	public Window_MainApp(String s, int w, int h) throws LWJGLException
	{
		setTitle(s);
		setSize(w, h);
		setLayout(new BorderLayout(5, 5));

		// setup OpenGL canvas
		add(canvas = new GeoCanvas(), BorderLayout.CENTER);
		
		addWindowStateListener(new WindowAdapter()
		{
			public void windowClosing(WindowEvent e)
			{
				System.out.println("Closing...");
				dispose();
				System.exit(0);
			}
		});
		
		// zooming in and out
		canvas.addMouseWheelListener(new MouseWheelListener()
		{
			@Override
			public void mouseWheelMoved(MouseWheelEvent e)
			{
//				getPreciseWheelRotation not avaiable in Java6 openjdk
				double delta = e.getWheelRotation();
				if (delta != 0)
				{
					double zoomAmount = delta/UIConst.MOUSE_WHEEL_SENSITIVITY;
					canvas.triangulation.zoom((float)zoomAmount);
				}
			}
		});
		
		// MOUSE MOTION and CLICK handlers
		if (UIConst.ENABLE_MOUSE_MESH_SELECTION)
		{
			
			canvas.addMouseMotionListener(new MouseMotionListener()
			{
				
				public void mouseMoved(MouseEvent e)
				{
//					System.out.println("e.getX() = " + e.getX());
//					System.out.println("e.getY() = " + e.getY());
				}
				
				@Override
				public void mouseDragged(MouseEvent e)
				{
					canvas.triangulation.isDragging = true;
					
					// move the mesh around
					if (canvas.triangulation.bottomright == null)
					{
						canvas.triangulation.bottomright = new Point(e.getX(), e.getY());
					}
					else
					{
						canvas.triangulation.bottomright.x = e.getX();
						canvas.triangulation.bottomright.y = e.getY();
						
//						System.out.println("UI Drag Window: " + canvas.triangulation.topleft + " , " + canvas.triangulation.bottomright);
//						System.out.println("GL Drag Window: ");
//						canvas.printGLPos(canvas.triangulation.topleft.x, canvas.triangulation.topleft.y);
//						canvas.printGLPos(canvas.triangulation.bottomright.x, canvas.triangulation.bottomright.y);
//						System.out.println();
					}
				}
			});
			
			canvas.addMouseListener(new MouseListener()
			{
				
				@Override
				public void mouseReleased(MouseEvent e)
				{
					// compute a range query if we are dragging and in that mode
					if (canvas.triangulation.isDragging && canvas.triangulation.isMode_RangeQuery())
					{
						FloatBuffer topleft = canvas.GetOGLPos(canvas.triangulation.topleft.x, canvas.triangulation.topleft.y);
						FloatBuffer bottomright = canvas.GetOGLPos(canvas.triangulation.bottomright.x, canvas.triangulation.bottomright.y);
						
						canvas.triangulation.searchRange_dragWindow(topleft.get(0), topleft.get(1), bottomright.get(0), bottomright.get(1));
						
						canvas.triangulation.isDragging = false;
					}
					

				}
				
				@Override
				public void mousePressed(MouseEvent e)
				{
					if (canvas.triangulation.topleft == null)
					{
						canvas.triangulation.topleft = new Point(e.getX(), e.getY());
					}
					else
					{
						canvas.triangulation.topleft.x = e.getX();
						canvas.triangulation.topleft.y = e.getY();
					}
				}
				
				@Override
				public void mouseExited(MouseEvent e)
				{
				}
				
				@Override
				public void mouseEntered(MouseEvent e) 
				{
				}
				
				@Override
				public void mouseClicked(MouseEvent e)
				{
					canvas.printGLPos(e.getX(), e.getY());
//					canvas.range_query(e.getX(), e.getY(), 10, 10);
				}
			});
		}

		
		canvas.addKeyListener(new KeyListener() {
			
			@Override
			public void keyTyped(KeyEvent e) {
				// TODO Auto-generated method stub
				
			}
			
			@Override
			public void keyReleased(KeyEvent e) {
				// TODO Auto-generated method stub
				
			}
			
			@Override
			public void keyPressed(KeyEvent e)
			{
				if (e.getKeyCode() == KeyEvent.VK_SPACE)
				{
					// reset the window query variables
					canvas.triangulation.topleft = null;
					canvas.triangulation.bottomright = null;
				}
				if (e.getKeyCode() == KeyEvent.VK_ENTER)
				{
//					canvas.triangulation.computeStatistics();
				}
					
				canvas.move_mesh(e);
			}
		});
		
		setupGUI();
		
		setResizable(false);
		setVisible(true);
		setDefaultCloseOperation(DISPOSE_ON_CLOSE);
	}
	
	public void setupGUI()
	{
		JMenuItem fileOpen = new JMenuItem("Open");
		fileOpen.addActionListener(new ActionListener()
		{
	        public void actionPerformed(ActionEvent e)
	        {
                int returnVal = fc.showOpenDialog(Window_MainApp.this);

                if (returnVal == JFileChooser.APPROVE_OPTION)
                {
                    File file = fc.getSelectedFile();
                    ArrayList<PointND> pointCloud = PointCloudReader.ReadFile(file.getAbsolutePath());
                    
                    canvas.triangulation.triangulate(pointCloud);
                    searchPanel.setMesh(canvas.triangulation);

                    setTitle(UIConst.APP_TITLE + " - " + file.getAbsolutePath());
                }
	        }
		});
		
		JMenuItem fileClose = new JMenuItem("Exit");
		fileClose.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event)  { System.exit(0); } });
		
		JMenuItem exportPointCloud = new JMenuItem("... as Point Cloud");
		exportPointCloud.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent arg0)
			{
				// TODO: file filters for point clouds (aka, .txt)
                int returnVal = fc.showSaveDialog(Window_MainApp.this);

                if (returnVal == JFileChooser.APPROVE_OPTION)
                {
                    File file = fc.getSelectedFile();
                    //This is where a real application would open the file.
                    System.out.print("Exporting: " + file.getName() + "...");
                    PointCloudWriter.WriteFile(file.getAbsolutePath(), canvas.triangulation.getCurrentPointSet());
                    System.out.println("done!");
                }
			}
		});
		
		JMenuItem exportCylinderCLoud = new JMenuItem("... as Cylinder Cloud (SnD)");
		exportCylinderCLoud.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent arg0)
			{
				// TODO: file filters for point clouds (aka, .txt)
                int returnVal = fc.showSaveDialog(Window_MainApp.this);

                if (returnVal == JFileChooser.APPROVE_OPTION)
                {
                    File file = fc.getSelectedFile();
                    //This is where a real application would open the file.
                    System.out.println("Exporting: " + file.getName() + "...");
                    
                    HashMap<String, String> factors = new HashMap<String, String>();
                    
                    for (int i = 0; i < 3; i++)
                    {
                        factors.put("SnD", ""+i);
                        
                        // TODO: sort this output by depth coordinate so that plane sweep will be easier?
                        ArrayList<PointND> cylinder = Resampler.convert_to_cylinder(canvas.triangulation, factors);
                        String savepath = file.getParent() + File.separatorChar + file.getName() + "_dim"+i;
                        PointCloudWriter.WriteFile_PointND(savepath, cylinder);
                    }

                    System.out.println("done!");
                }
			}
		});
		
		JMenuItem exportEpsNetCloud = new JMenuItem("... using eps-Net Reduction");
		exportEpsNetCloud.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent arg0)
			{
				// TODO: display reduction the options
				Object[] possibleValues = { "SnD", "curv_mean", "curv_gaus", "curv_ansio", "curv_both", "SnD_Curv", "normal", "Just XYZ"};
				Object selectedValue = JOptionPane.showInputDialog(
						null,
						"Additional Dimensions: ", "eps-Net Reduction",
						JOptionPane.INFORMATION_MESSAGE, null,
						possibleValues, possibleValues[0]);
				
                int returnVal = fc.showSaveDialog(Window_MainApp.this);

                if (returnVal == JFileChooser.APPROVE_OPTION)
                {
                    File file = fc.getSelectedFile();
                    //This is where a real application would open the file.
                    System.out.println("Exporting: " + file.getName() + "...");
                    
                    ArrayList<PointND> epsNet = Resampler.epsilonNet_reduction(canvas.triangulation, (String)selectedValue);
                    String savepath = file.getParent() + File.separatorChar + file.getName() + "_epsNet";
                    PointCloudWriter.WriteFile_PointND(savepath, epsNet);
                    
                    System.out.println("done!");
                }
			}
		});
		
		JMenuItem exportImage = new JMenuItem("... as Image");
		exportImage.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				// TODO: file filters for images
                int returnVal = fc.showSaveDialog(Window_MainApp.this);

                if (returnVal == JFileChooser.APPROVE_OPTION)
                {
                	File file = fc.getSelectedFile();
                	canvas.takeScreenshot(file);
                }
			}
		});
		
		JMenu export = new JMenu("Export Mesh");
		export.add(exportPointCloud);
		export.add(exportCylinderCLoud);
		export.add(exportEpsNetCloud);
		export.add(exportImage);
		
		menu_file = new JMenu("File");
		menu_file.add(fileOpen);
		menu_file.add(export);
		menu_file.add(new JSeparator());
		menu_file.add(fileClose);
		
		
		JMenuItem histStrike = new JMenuItem("Strike");
		histStrike.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent e)
			{
				HistogramViewer viewer = new HistogramViewer("Histogram Viewer");
				ArrayList<STLTriangle> triangles = canvas.triangulation.getTriangleSet();
				HistogramViewer.createHistogram_Strike(viewer, triangles, canvas.triangulation.dLaserStrike, canvas.triangulation.dLaserStrike);
				viewer.setVisible(true);
			}
		});
		
		JMenuItem histDip = new JMenuItem("Dip");
		histDip.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent e)
			{
				HistogramViewer viewer = new HistogramViewer("Histogram Viewer");
				ArrayList<STLTriangle> triangles = canvas.triangulation.getTriangleSet();
				HistogramViewer.createHistogram_Dip(viewer, triangles, canvas.triangulation.dLaserStrike, canvas.triangulation.dLaserStrike);
				viewer.setVisible(true);
			}
		});
		
		JMenuItem histArea = new JMenuItem("Triangle Area");
		histArea.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent e)
			{
				HistogramViewer viewer = new HistogramViewer("Histogram Viewer");
				ArrayList<STLTriangle> triangles = canvas.triangulation.getTriangleSet();
				HistogramViewer.createHistogram_TriangleSize(viewer, triangles);
				viewer.setVisible(true);
			}
		});
		
		menu_histograms = new JMenu("Histograms");
		menu_histograms.add(histStrike);
		menu_histograms.add(histDip);
		menu_histograms.add(histArea);
		
		menu_bar = new JMenuBar();
		menu_bar.add(menu_file);
		menu_bar.add(menu_histograms);
		setJMenuBar(menu_bar);
		
		// add a panel at the bottom
		searchPanel = new OptionsPanel();
		
		add(searchPanel, BorderLayout.SOUTH);
	}
	
	// testing code
	public static void main(String[] args) throws LWJGLException
	{
		Window_MainApp frame = new Window_MainApp(UIConst.APP_TITLE, UIConst.APP_WIDTH, UIConst.APP_HEIGHT);
//		ArrayList<PointND> p = DataGenerator.generateSemiFlat(100, 100, -500, 5);
//		ArrayList<PointND> p = DataGenerator.generatePerfectFlat(100, 100, -500);
//		ArrayList<PointND> p = DataGenerator.generatePerfectFlat(25, 25, -500);
//		ArrayList<PointND> p = DataGenerator.generateBedding(6, 100, 10, 20, 1, 10, -500, -499);
		
//		ArrayList<PointND> p = DataGenerator.generateHalfCylinder(100, 100, 0.01);
//		ArrayList<PointND> p = DataGenerator.generateCube(100);
//		ArrayList<PointND> p = DataGenerator.generatePyramid(0,200);
		
		ArrayList<PointND> p = DataGenerator.generateCone(50, 50, 20);
		
		
		

//		String voxelFilePath = "data"+File.separator+"scan_center_1to1_fill"+PointCloudReader.VOXEL_FILE_EXT;
//		ArrayList<PointND> pointCloud = PointCloudReader.ReadFile(voxelFilePath);
		ArrayList<PointND> pointCloud;

		pointCloud = p;
		
		System.out.println(Math.atan2(0,0));
		
		frame.canvas.triangulation = new SimpleTriangulation();
		frame.canvas.triangulation.triangulate(pointCloud);
		frame.searchPanel.setMesh(frame.canvas.triangulation);
	}
}
